package mcontext

import (
	"context"
	"gitee.com/dennis-mxx/gcode-common/mlogger"
)

var MTX_CONTEXT_KEY = "mtx_context"
var MTX_CONTEXT_PARENT_KEY = "mtx_context_parent"
var MTX_LOGGER_KEY = "mtx_logger"

type MContext struct {
	logger *mlogger.TraceLogger
	attr   map[string]any
}

func (ew *MContext) Logger() *mlogger.TraceLogger {
	return ew.logger
}

func (ew *MContext) AppendTraceId(traceId string) *mlogger.TraceLogger {
	ew.logger.Append(mlogger.TRACE_ID, traceId)
	return ew.logger
}
func (ew *MContext) SetTraceId(traceId string) *mlogger.TraceLogger {
	ew.logger.Remove(mlogger.TRACE_ID)
	ew.logger.Append(mlogger.TRACE_ID, traceId)
	return ew.logger
}
func WithLogger(logger *mlogger.TraceLogger) *MContext {
	return &MContext{logger: mlogger.WithLogger(logger), attr: map[string]any{}}
}

func (ew *MContext) Value(key string) any {
	if result := ew.attr[key]; result != nil {
		return result
	}
	if parent := ew.attr[MTX_CONTEXT_PARENT_KEY]; parent != nil {
		if item, ok := parent.(context.Context); ok {
			return item.Value(key)
		}
	}
	return nil
}
func (ew *MContext) Set(key string, value any) {
	ew.attr[key] = value
}

func (ew *MContext) Remove(key string) {
	delete(ew.attr, key)
}
func (ew *MContext) WithParent(parent context.Context) {
	ew.attr[MTX_CONTEXT_PARENT_KEY] = parent
}
func (ew *MContext) Context() context.Context {
	if parent := ew.attr[MTX_CONTEXT_PARENT_KEY]; parent != nil {
		ctx := parent.(context.Context)
		for k, v := range ew.attr {
			ctx = context.WithValue(ctx, k, v)
		}
		return ctx
	} else {
		ctx := context.Background()
		for k, v := range ew.attr {
			ctx = context.WithValue(ctx, k, v)
		}
		return ctx
	}
}
func (ew *MContext) Commit(clear bool) {
	var remove []string
	logger := ew.logger
	for key, v := range ew.attr {
		if committer, ok := v.(Commiter); ok {
			remove = append(remove, key)
			go func(k string, c Commiter) {
				if err := c.Commit(); err != nil {
					logger.Error("commit error:", k, " -- ", err)
				}
			}(key, committer)
		}
	}
	if clear {
		for i := range remove {
			ew.Remove(remove[i])
		}
	}
}

func (ew *MContext) Close(clear bool) {
	var remove []string
	logger := ew.logger
	for key, v := range ew.attr {
		if closer, ok := v.(Closer); ok {
			remove = append(remove, key)
			go func(k string, c Closer) {
				if err := c.Close(); err != nil {
					logger.Error("close error:", k, " -- ", err)
				}
			}(key, closer)
		}
	}
	if clear {
		for i := range remove {
			ew.Remove(remove[i])
		}
	}
}
func WithContext(ctx context.Context) *MContext {
	var result *MContext
	if mtx := ctx.Value(MTX_CONTEXT_KEY); mtx != nil {
		return mtx.(*MContext)
	}
	if logger := ctx.Value(MTX_LOGGER_KEY); logger != nil {
		result = WithLogger(logger.(*mlogger.TraceLogger))
	} else {
		result = &MContext{logger: mlogger.DefaultTraceLogger(), attr: map[string]any{}}
	}
	result.WithParent(ctx)
	return result
}
func WithMContext(mtx *MContext) *MContext {
	attr := make(map[string]any)
	for k := range mtx.attr {
		attr[k] = mtx.attr[k]
	}
	return &MContext{logger: mlogger.WithLogger(mtx.logger), attr: attr}
}
